package dbmanager

import (
	"reflect"
	"github.com/astaxie/beego/orm"
	"fmt"
	"cloud_worm/util/umodels/mysqldb"
	"strings"
	"cloud_worm/util/umodels/mysqldb/dbdata"
)

type Machine struct {
	Id 			int64

	ApplyId 	int64
	Ip      	string 		// 机器ip
	WormId   	string 		// 机器 worm id
	Score   	int    		//主机的安全评分

	Created 	int64
	Updated 	int64
	Status  	int
}

func (w *Machine) TableName() string {
	return reflect.TypeOf(*w).Name()
}

func (w *Machine) Read(fields ...string) error {
	err := orm.NewOrm().Read(w, fields...)
	if err != nil {
		// todo: 具体错误处理，参考(https://beego.me/docs/mvc/model/object.md)
		// orm.ErrNoRows
		return fmt.Errorf("%s, %s", w.TableName(), err.Error())
	}
	return nil
}

func (w *Machine) Update(fields ...string) error {
	_, err := orm.NewOrm().Update(w, fields...)
	if err != nil {
		return fmt.Errorf("%s, %s", w.TableName(), err.Error())
	}
	return nil
}

func (w *Machine) Insert() error {
	_, err := orm.NewOrm().Insert(w)
	if err != nil {
		return fmt.Errorf("%s, %s", w.TableName(), err.Error())
	}
	return nil
}

func init()  {
	orm.RegisterModel(new(Machine))
}

func (w *Machine) Count(status int, applyId int64, ip string, wolId string) (int64, error) {
	qs := orm.NewOrm().QueryTable(w)

	if applyId != mysqldb.SelectIntAll {
		qs = qs.Filter("apply_id", applyId)
	}

	if ip != mysqldb.SelectStrAll {
		// 设置ip条件
		qs = qs.Filter("ip__istartswith", ip)
	}
	if wolId != mysqldb.SelectStrAll {
		// 设置wol_id条件
		qs = qs.Filter("worm_id__istartswith", wolId)
	}
	if status != mysqldb.SelectIntAll {
		qs = qs.Filter("status", status)
	}

	count, err := qs.Count()
	if err != nil {
		return 0, err
	}
	return count, nil
}

/**
基于条件选择
 */
func (w *Machine) Select(offset int, limit int, status int, applyId int64,
	ip string, wolId string) ([]*Machine, error) {
	var machines []*Machine

	qs := orm.NewOrm().QueryTable(w).Offset(offset).Limit(limit)
	if applyId != mysqldb.SelectIntAll {
		qs = qs.Filter("apply_id", applyId)
	}
	if ip != mysqldb.SelectStrAll {
		// 设置ip条件
		qs = qs.Filter("ip__istartswith", ip)
	}
	if wolId != mysqldb.SelectStrAll {
		// 设置wol_id条件
		qs = qs.Filter("worm_id__istartswith", wolId)
	}

	if status != mysqldb.SelectIntAll {
		qs = qs.Filter("status", status)
	}

	_, err := qs.All(&machines)
	if err != nil {
		return nil, err
	}
	return machines, nil
}

type IPAndWormId struct {
	Ip          	string
	WormId       	string
}

func (w *Machine) GetIPsByApplyID(status int, applyID int) ([]*IPAndWormId, error) {
	qb, err := orm.NewQueryBuilder("mysql")
	if err != nil {
		return nil, err
	}

	ipField := fmt.Sprintf("%s.%s", w.TableName(), "ip")
	wolIdField := fmt.Sprintf("%s.%s", w.TableName(), "wol_id")
	qb = qb.Select(ipField, wolIdField).From(w.TableName()).Where(mysqldb.SelectCondTrue)

	if applyID != mysqldb.SelectIntAll {
		qb = qb.And(fmt.Sprintf("apply_id=%d", applyID))
	}
	if status != mysqldb.StatusAll {
		qb = qb.And(fmt.Sprintf("status=%d", status))
	}

	sql := qb.String()

	var ips []*IPAndWormId
	_, err = orm.NewOrm().Raw(sql).QueryRows(&ips)
	if err != nil {
		return nil, err
	}

	return ips, nil
}

// @Description: wolagent的状态和版本信息
type StatusVersion struct {
	AgentStatus      int         // wolagent的在线状态，0：online；1：offline；100：invalid
	AgentVersion     int         // 版本号
}
type MachineBrief struct {
	ID                  int64
	IP                  string
	WormId              string
	Score               int                     // 机器的安全评分
	Status              int                     // 机器状态
	Alert               int                     // 机器的告警项
	StatusVersion
}

/**
 @Description：基于application id获取应用下ip
 @Param:
 @Return：
 */
func (w *Machine) GetMachinesBrief(offset int, limit int, status int, applyId int64,
	ip string, wolId string) ([]*MachineBrief, error) {
	// 取出机器
	ms, err := w.Select(offset, limit, status, applyId, ip, wolId)
	if err != nil {
		return nil, err
	}

	svs, err := w.getAgentStatusVersion(ms)
	if err != nil {
		return nil, err
	}
	alerts, err := w.getMachineAlert(ms)
	if err != nil {
		return nil, err
	}

	var b []*MachineBrief
	for _, tmp := range ms {
		one := &MachineBrief{
			ID: tmp.Id,
			IP: tmp.Ip,
			WormId: tmp.WormId,
			Score: tmp.Score,
			Status: tmp.Status,
		}
		_, exist := alerts[tmp.Ip]
		if exist == true {
			one.Alert = alerts[tmp.Ip]
		} else {
			one.Alert = 0
		}

		_, exist = svs[tmp.WormId]
		if exist == true {
			one.StatusVersion = svs[tmp.WormId]
		} else {
			one.AgentStatus = AgentInvalid
		}

		b = append(b, one)
	}

	return b, nil
}

/**
 @Description：基于指定的machines得到agent的status和version
 @Param:
 @Return：
 */
func (w *Machine) getAgentStatusVersion(ms []*Machine) (map[string]StatusVersion, error) {
	var ips []string
	for _, item := range ms {
		ips = append(ips, fmt.Sprintf("'%s'", item.Ip))
	}

	// 获取wolagent offline/online、version信息
	qb, err := orm.NewQueryBuilder("mysql")
	if err != nil {
		return nil, err
	}
	worm := &WormId{}
	agentWolId := fmt.Sprintf("%s.%s", worm.TableName(), "wolverine_id")
	agentStatusField := fmt.Sprintf("%s.%s", worm.TableName(), "status")
	agentVersion := fmt.Sprintf("%s.%s", worm.TableName(), "version")
	qb = qb.Select(agentWolId, agentStatusField, agentVersion).From(worm.TableName())

	qb = qb.Where(fmt.Sprintf("status!=%d", AgentInvalid))
	qb = qb.And(fmt.Sprintf("ip in(%s)", strings.Join(ips, ",")))
	sql := qb.String()

	type AgentStatusVersion struct {
		WormId         string
		Status              int
		Version             int
	}
	var agents []*AgentStatusVersion
	_, err = orm.NewOrm().Raw(sql).QueryRows(&agents)
	if err != nil {
		return nil, err
	}

	svs := make(map[string]StatusVersion)
	for _, tmp := range agents {
		svs[tmp.WormId] = StatusVersion{AgentStatus: tmp.Status, AgentVersion: tmp.Version}
	}
	return svs, nil
}

/**
 @Description：基于指定的machines得到对应的alert信息
 @Param:
 @Return：
 */
func (w *Machine) getMachineAlert(ms []*Machine) (map[string]int, error) {
	qb, err := orm.NewQueryBuilder("mysql")
	if err != nil {
		return nil, err
	}

	ac := &dbdata.WeaponAlertCount{}
	ipField := fmt.Sprintf("%s.%s", ac.TableName(), "ip")
	optionFailedField := fmt.Sprintf("%s.%s", ac.TableName(), "option_failed")
	qb = qb.Select(ipField, optionFailedField).From(ac.TableName()).Where(mysqldb.SelectCondFalse)
	for _, tmp := range ms {
		qb = qb.Or(fmt.Sprintf("ip='%s'", tmp.Ip))
	}
	sql := qb.String()

	type IPOptionFailed struct {
		Ip              string
		OptionFailed    int
	}
	var ofs []*IPOptionFailed
	_, err = orm.NewOrm().Raw(sql).QueryRows(&ofs)
	if err != nil {
		return nil, err
	}
	alerts := make(map[string]int)
	for _, tmp := range ofs {
		_, exist := alerts[tmp.Ip]
		if exist == false {
			alerts[tmp.Ip] = tmp.OptionFailed
		} else {
			alerts[tmp.Ip] = alerts[tmp.Ip] + tmp.OptionFailed
		}
	}
	return alerts, nil
}

/**
 @Description：自定义一个list，用于服务sort的条件处理
 */
type MachineBriefList []*MachineBrief

/**
 @Description：获取list的len
 @Param:
 @Return：
 */
func (l MachineBriefList) Len() int {
	return len(l)
}

/**
 @Description：比较less，按照alert由大到小的顺序处理
 @Param:
 @Return：
 */
func (l MachineBriefList) Less(i, j int) bool {
	if l[i].Alert >= l[j].Alert {
		return true
	}

	return false
}

/**
 @Description：执行交换
 @Param:
 @Return：
 */
func (l MachineBriefList) Swap(i, j int)  {
	tmp := l[i]
	l[i] = l[j]
	l[j] = tmp
	return
}